package main

import "fmt"

func main() {
	// map 声明
	// map[string]string, key与value元素的类型相同
	// map[int]string, key与value元素的类型不同

	// key 类型必须支持 == 和 != 两种比较运算符
	// 函数类型、map类型自身以及切片类型时不能作为map的key类型的

	// 初值为零值nil的切片类型变量, 可以借助内置的append的函数进行操作, 这种在Go语言中被称为 "零值可用"
	// 但是 map 类型, 因为它内部实现的复杂性, 无法"零值可用"

	// 使用复合字面值初始化map类型变量
	m := map[int]string{}
	m1 := map[int][]string{
		1: []string{"val1_1", "val1_2"},
		3: []string{"val3_1", "val3_2", "val3_3"},
		7: []string{"val7_1"},
	}

	type Position struct {
		x float64
		y float64
	}

	m2 := map[Position]string{
		Position{29.935523, 52.568915}:  "school",
		Position{25.352594, 113.304361}: "shopping-mall",
		Position{73.224455, 111.804306}: "hospital",
	}
	// Go允许省略字面值中的元素类型
	m2 = map[Position]string{
		{29.935523, 52.568915}:  "school",
		{25.352594, 113.304361}: "shopping-mall",
		{73.224455, 111.804306}: "hospital",
	}
	fmt.Println(m1)
	fmt.Println(m2)

	// 使用make为map类型变量进行显式初始化
	m1 = make(map[int][]string)       // 未指定初始容量
	m2 = make(map[Position]string, 8) // 指定初始容量为8

	// 插入键值对
	// 开发人员不需要判断是否插入成功, 因为Go会保证插入总是成功的。这里,Go运行时会负责map变量内部的内存管理。
	// 除非是系统内存耗尽, 可以不用担心想map中插入新数据的数量和执行结果
	m[1] = "value1"
	m[2] = "value2"
	m[3] = "value3"

	m[1] = "value01" // value01 会覆盖掉1对应的旧值
	m[4] = "value4"  // 此时 m 为 map[1:value01, 2:value2, 3:value3, 4:value4]

	// 获取键值对数量
	fmt.Println(len(m)) // 4

	// 查找和数据读取
	// 如果key不在map中, 那么v会被赋值为对应类型的零值, 需要第二个返回值来判断是否在map中
	v, ok := m[1]
	if ok {
		// 1 在 map 中
		fmt.Println(v)
	} else {
		// 1 不在mao中
		fmt.Println("不在map中")
	}

	// 删除数据
	fmt.Println(m) // map[1:value01 2:value2 3:value3 4:value4]
	delete(m, 2)
	fmt.Println(m) // map[1:value01 3:value3 4:value4]

	// 遍历数据
	// 对同一map做多次便利的时候, 每次便利元素的次序都不相同
	// 程序逻辑不要依赖map所得到的元素次数
	for k, v := range m {
		fmt.Printf("[%d,%s].\n", k, v)
	}

	// 考虑到map 可以自动扩容, map中数据元素的value位置可能在这一过程发生变化, 所以Go不允许获取map中value的地址，这个约束是在编译期间就生效的。
}
